package service

import (
	"context"
	"errors"
	"fmt"
	"github.com/alomerry/copier"
	"strings"
	"time"

	"github.com/alomerry/home/core/extension"
	"github.com/alomerry/home/core/utils"
	"github.com/alomerry/home/core/validators"
	"github.com/alomerry/home/proto/account"
	"github.com/alomerry/home/service/account/model"
	"github.com/spf13/cast"
)

const (
	UserLoginLockKey      string = "%s:login-failed-times"
	UserLoginLockDuration int16  = 3600
	UserMaxFailedTimes    int8   = 5
)

func (AccountService) Login(ctx context.Context, req *account.LoginRequest) (*account.LoginResponse, error) {
	if err := validators.ValidateRequest(req); err != nil {
		return nil, err
	}

	user, err := validateUser(ctx, req)
	if err != nil {
		return nil, err
	}

	err = user.UpdateLoginTime()
	if err != nil {
		panic(err)
	}

	// 保存用户登录记录
	err = model.CLoginLog.Create(user.Username, "")
	if err != nil {
		return nil, err
	}

	token, err := utils.GeneratePortalAccessToken(ctx, req.Account)
	if err != nil {
		return nil, err
	}

	// TODO cache user info

	resp := &account.LoginResponse{
		AccessToken: token,
		ExpireAt:    time.Now().Add(utils.AccessTokenPortalExpireTime).Local().Format("2006-01-02 15:04:05"),
		User:        &account.User{},
	}

	_ = copier.Instance(nil).From(user).CopyTo(&resp.User)

	//config, err := model.CUserConfig.GetByUserId(ctx, user.ID)
	//if err != nil {
	//	return nil, err
	//}
	//

	//protoConfig := &account.UserConfig{}
	//_ = copier.Instance(nil).From(config).CopyTo(protoConfig)
	//resp.User.Config = protoConfig
	return resp, nil
}

func validateUser(ctx context.Context, req *account.LoginRequest) (*model.User, error) {
	key := fmt.Sprintf(UserLoginLockKey, req.Account)
	value, _ := extension.GetRDB().Get(ctx, key).Result()
	times := cast.ToInt8(value)
	if times >= UserMaxFailedTimes {
		return nil, errors.New("too many  times")
	}

	user, err := model.CUser.GetByUserName(ctx, strings.ToLower(req.Account))
	if err != nil {
		return nil, err
	}

	if user.Password != utils.Md5(req.Password) {
		return nil, errors.New("用户名或密码错误")
	}

	if user.Locked() {
		return nil, errors.New("账号已锁定")
	}

	_, _ = extension.GetRDB().SetEx(ctx, key, cast.ToString(cast.ToInt8(times)+1), time.Duration(UserLoginLockDuration)).Result()
	return user, nil
}
